﻿

using System;
using System.Text;
using System.Reflection;
using Microsoft.Practices.Unity.InterceptionExtension;
using System.Runtime.Caching;

using System.Collections.Generic;

namespace DanGao.Infrastructure.Cache
{
    public class CacheBehavior : IInterceptionBehavior
    {
        private string GetValueKey(IMethodInvocation input)
        {
            if(input.Arguments != null && input.Arguments.Count != 0)
            {
                var sb = new StringBuilder();
                for (int i = 0; i < input.Arguments.Count;i++ )
                {
                    sb.Append(input.Arguments[i].ToString() + (i + 1 == input.Arguments.Count ? "" : "_"));
                }
                return sb.ToString();
            }
            return null;
        }

        public System.Collections.Generic.IEnumerable<Type> GetRequiredInterfaces()
        {
            return Type.EmptyTypes;
        }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            IMethodReturn ret = null;
            var method = input.MethodBase;
            var key = method.Name;
            if (method.IsDefined(typeof(CacheAttribute), false))
            {
                var cacheattribute = (CacheAttribute)(CacheAttribute)method.GetCustomAttributes(typeof(CacheAttribute), false)[0];
                var keyValue = GetValueKey(input);
                switch (cacheattribute.Type)
                {
                    case CacheType.Get:
                        try
                        {
                            if (CacheManage.Exists(key, keyValue))
                            {
                                var obj = CacheManage.Get(key, keyValue);
                                var arg = new object[input.Arguments.Count];
                                input.Arguments.CopyTo(arg, 0);
                                ret = new VirtualMethodReturn(input, obj, arg);
                            }
                            else
                            {
                                var methodReturn = getNext().Invoke(input, getNext);
                                CacheManage.Add(key, keyValue, methodReturn.ReturnValue);
                                ret = methodReturn;
                            }
                        }
                        catch (Exception ex)
                        {
                            ret = new VirtualMethodReturn(input, ex);
                        }
                        break;
                    case CacheType.Put:
                        try
                        {
                            var methodReturn = getNext().Invoke(input, getNext);
                            if (CacheManage.Exists(key, keyValue))
                            {
                                CacheManage.Remove(key);
                            }
                            CacheManage.Add(key, keyValue, methodReturn.ReturnValue);
                            ret = methodReturn;
                        }
                        catch (Exception ex)
                        {
                            ret = new VirtualMethodReturn(input, ex);
                        }
                        break;
                    case CacheType.Remove:
                        try
                        {
                            var keys = cacheattribute.Methods;
                            foreach(var item in keys)
                            {
                                if (CacheManage.Exists(item))
                                {
                                    CacheManage.Remove(item);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            ret = new VirtualMethodReturn(input, ex);
                        }
                        break;
                    default:
                        break;
                }
                return ret;
            }
            return getNext().Invoke(input, getNext);
        }

        public bool WillExecute
        {
            get { return true; }
        }
    }

}
